/*! jQuery tinyklass private v0.3a - Jean-Louis Grall - MIT license - http://code.google.com/p/jquery-tinyklass-plugin */

(function( $, undefined ) {

var securedKlasses = [ undefined ],
	reverseArray = securedKlasses.reverse,	// Array.prototype.reverse
	reverse = function( array ) {
		return reverseArray.call( array );
	},
	//apply = reverse.apply,	// Function.apply
	$klass_orig = $.klass,	// Keep a reference to the original $.klass
	$klass = $.klass = function( _super, fields ) {
		if ( !fields ) {
			fields = _super;
			_super = undefined;
		}
		
		var _private = fields._private,
			secure = false,
			makeSecuredMethods = [],
			secured = [],
			generateInit,
			INIT,
			originalInit,
			klassInfos,
			p = _super,
			len = securedKlasses.length,
			found,
			originalMethods,
			name,
			originalMethod,
			publicMethod,
			klass,
			i;
		if( _super ) {
			if( _super._klass.privateInit ) generateInit = true;
			do {
				for ( found = false, i = len - 1; i > 0 && !found; i-- ) {
					if ( securedKlasses[i].klass === p ) len = found = i;
				}
				secured[ secured.length ] = found;
			} while ( p = p._super );
			reverse( secured );
		}
		
		if ( _private ) {
			generateInit = true;
			originalMethods = {};
			
			if ( _private._secure === true ) {
				secure = true;
				delete _private._secure;
			}
			delete fields._private;
			
			for ( name in _private ) {
				originalMethod = _private[ name ];
				
				publicMethod = _createPublicMethod( secured.length, originalMethod );
				makeSecuredMethods[ makeSecuredMethods.length ] = publicMethod[1];
				publicMethod = publicMethod[0];
				
				originalMethods[ name ] = originalMethod;
				_private[ name ] = publicMethod;
			}
			
			$.extend( fields, _private );
		}
			
		if ( generateInit ) {
			originalInit = fields.init;
			INIT = fields.init = function() {
				
				if ( this.init === klass ) {
					var self = this,
						privateProps = [],
						i;
					for ( i in secured) privateProps[i] = {};
					
					this._callPrivateProps = function( klassNb, originalMethod, args ) {
						if ( secured[ klassNb ] ) {
							originalMethod = securedKlasses[ secured[ klassNb ] ][ originalMethod ];
							//if ( originalMethod.apply !== apply ) $.error( "Security error: forbidden method call" );
						}
						
						var privateArgs = [ privateProps[ klassNb ] ], i;
						for ( i = args.length; i > 0; i-- ) privateArgs[i] = args[ i - 1 ];	// http://jsperf.com/small-array-concatenation
						return originalMethod.apply( self, privateArgs );	// Better than: apply.apply( originalMethod, [self, privateArgs] );
					};
				}
				
				if ( originalInit ) originalInit.apply( this, arguments );
				else this._super.apply( this, $.merge( [ arguments ], arguments ) );
			};
		}
		
		klass = $klass_orig( _super, fields );	// Make the new class
		
		if ( INIT ) {
			klass._klass.privateInit = true;
			secured[ secured.length ] = secure && securedKlasses.length;
			if ( originalInit ) originalInit._klass = INIT._klass;
			if ( secure ) {
				klass._klass.privateSecure = true;
				klassInfos = [];
				klassInfos.klass = klass;
				securedKlasses[ securedKlasses.length ] = klassInfos;
			}
		}
		if ( _private ) {
			for ( name in _private ) {
				originalMethod = originalMethods[ name ];
				publicMethod = _private[ name ];
				publicMethod._klass.publicMethod = publicMethod;
				originalMethod._klass = publicMethod._klass;
			}
			if ( secure ) {
				for ( i in makeSecuredMethods ) {
					securedKlasses[ secured[ secured.length - 1 ] ][i] = makeSecuredMethods[i]( i );
				}
			}
		}
		
		return klass;
	},
	$proto = $klass.prototype = $klass_orig.prototype,
	_createPublicMethod = function( klassNb, originalMethod ) {	// Creates a public method that will call the private method.
		return [
			function() {
				return this._callPrivateProps.call( this, klassNb, originalMethod, arguments );
			},
			function( secureMethodId ) {	// Called if needed to secure the originalMethod
				var method = originalMethod;
				originalMethod = secureMethodId;
				return method;
			}
		];
	};

})( jQuery );